package com.github.panchitoboy.shiro.wechat.filter;

import java.io.IOException;

import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.io.IOUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.IncorrectCredentialsException;
import org.apache.shiro.authc.UnknownAccountException;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.authc.AuthenticatingFilter;
import org.apache.shiro.web.util.WebUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.alibaba.fastjson.JSONObject;
import com.github.panchitoboy.shiro.wechat.repository.WechatUserRepository;
import com.thinkgem.jeesite.common.utils.StringUtils;

public final class WechatJWTOrFormAuthenticationFilter extends AuthenticatingFilter {

	@Autowired
	private WechatUserRepository wechatUserRepository;
	 
    public static final String PHONE_NUMBER = "phoneNumber";
    public static final String CAPTCHA = "captcha";
    public static final String CODE = "code";
    public static final String USERNAME = "username";
    public static final String PASSWORD = "password";
    
    protected static final String AUTHORIZATION_HEADER = "Authorization";

    public WechatJWTOrFormAuthenticationFilter() {
        setLoginUrl(DEFAULT_LOGIN_URL);
    }
    
    @Override
    protected boolean executeLogin(ServletRequest request, ServletResponse response) throws Exception {
        AuthenticationToken token = createToken(request, response);
        if (token == null) {
            String msg = "请检查您的token。";
            return onLoginFailure(token, new AuthenticationException(msg), request, response);
        }
        try {
            Subject subject = getSubject(request, response);
            subject.login(token);
            return onLoginSuccess(token, subject, request, response);
        } catch (AuthenticationException e) {
            return onLoginFailure(token, e, request, response);
        }
    }    

    @Override
    public void setLoginUrl(String loginUrl) {
        String previous = getLoginUrl();
        if (previous != null) {
            this.appliedPaths.remove(previous);
        }
        super.setLoginUrl(loginUrl);
        this.appliedPaths.put(getLoginUrl(), null);
    }
    
    /**
     * onAccessDenied：表示当访问拒绝时是否已经处理了；如果返回 true 表示需要继续处理；如果返回 false 表示该拦截器实例已经处理了，将直接返回即可。
     */
    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        boolean loggedIn = false;

        if (isLoginRequest(request, response) || isLoggedAttempt(request, response)) {
            loggedIn = executeLogin(request, response);
        }

        if (!loggedIn) {
            HttpServletResponse httpResponse = WebUtils.toHttp(response);
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        }
        
        /**
         * 用户没有资源权限
         */
        /*if(!isLoginRequest(request, response) && !ResourceUtils.matches(WebUtils.toHttp(request).getRequestURI() , WebUtils.toHttp(request).getMethod())) {
        	HttpServletResponse httpResponse = WebUtils.toHttp(response);
            httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
        }*/

        return loggedIn;
    }
    
    /**
     * 这个方法主要为了覆盖父类中的方法，每次都需要进行验证登录
     * isAccessAllowed：表示是否允许访问；mappedValue 就是[urls]配置中拦截器参数部分，如果允许访问返回 true，否则 false；
     */
    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) {
        boolean loggedIn = false;
        
        if(isLoginRequest(request, response)) {
        	//Subject subject = getSubject(request, response);
        	//loggedIn = subject.isAuthenticated();
        }

        return loggedIn;
    }

    @Override
    protected AuthenticationToken createToken(ServletRequest request, ServletResponse response) throws IOException {

        if (isLoginRequest(request, response)) {
            String json = IOUtils.toString(request.getInputStream());

            if (json != null && !json.isEmpty()) {
            	JSONObject jsonObject =  JSONObject.parseObject(json);
            	String phoneNumber = jsonObject.getString(PHONE_NUMBER);
            	String code = jsonObject.getString(CODE);
                String captcha = jsonObject.getString(CAPTCHA) != null ? jsonObject.getString(CAPTCHA): "";
                String username = jsonObject.getString(USERNAME);
                String password = jsonObject.getString(PASSWORD);
        		return new WechatJWTAuthenticationToken(phoneNumber, captcha, code,username,password);
            }
        }

        if (isLoggedAttempt(request, response)) {
            String jwtToken = getAuthzHeader(request);
            if (jwtToken != null) {
                return createToken(jwtToken);
            }
        }

        return null;
    }

    @Override
    protected boolean onLoginFailure(AuthenticationToken token, AuthenticationException e, ServletRequest request, ServletResponse response) {

    	String className = e.getClass().getName(), message = "";
    	if (isLoginRequest(request, response)){
			if (IncorrectCredentialsException.class.getName().equals(className)
					|| UnknownAccountException.class.getName().equals(className)){
				message = "手机号或验证码错误, 请重试.";
			}
			else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "msg:")){
				message = StringUtils.replace(e.getMessage(), "msg:", "");
			}
			else if (e.getMessage() != null && StringUtils.startsWith(e.getMessage(), "No account information found for authentication token")) {
				message = "手机号或验证码错误, 请重试.";
			}
			else{
				message = "系统出现点问题，请稍后再试！";
				e.printStackTrace(); // 输出到控制台
			}
    	}
    	if(isLoggedAttempt(request, response)) {
    		if (AuthenticationException.class.getName().equals(className)){
				message = e.getMessage();
			} else {
				message = "验证失败，请先登录！";
			}
    	}
        HttpServletResponse httpResponse = WebUtils.toHttp(response);
        httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        httpResponse.setContentType("application/json");
        try {
			httpResponse.getWriter().write("{\"code\": 1, \"message\":\"" + message + "\"}");
			httpResponse.getWriter().close();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			e1.printStackTrace();
		}
        return false;
    }
    
    /**
     * 判断用户是否想要登入。
     * 检测header里面是否包含Authorization字段即可
     */
    protected boolean isLoggedAttempt(ServletRequest request, ServletResponse response) {
        String authzHeader = getAuthzHeader(request);
        return authzHeader != null;
    }

    protected String getAuthzHeader(ServletRequest request) {
        HttpServletRequest httpRequest = WebUtils.toHttp(request);
        return httpRequest.getHeader(AUTHORIZATION_HEADER);
    }

    public WechatJWTAuthenticationToken createToken(String token) {
    	//如果token为空或者前端传来null，则返回null
    	if(StringUtils.isBlank(token) || token.equals("null")) {
    		return null;
    	}
        String phoneNumber = wechatUserRepository.getSubject(token);
        return new WechatJWTAuthenticationToken((Object)phoneNumber, token);
    }
    
    /**
	 * 登录成功之后跳转URL
	 */
	public String getSuccessUrl() {
		return super.getSuccessUrl();
	}
}
